package completablefuture;

import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author ZhuQi
 * @ClassName Demo3
 * @Date 2022/9/6 10:12
 * @Version 1.0
 * @Description api
 */
public class Demo3 {

    /**
     * 测试 complete 打断计算 直接基于 T t 值
     */
    @Test
    @SneakyThrows
    public void test01() {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务完成";
        });

        // complete 打断式获取，如果任务未完成 直接给予 入参 t 做为返回值
        System.out.println("complete:" + completableFuture.complete("任务未完成"));
        System.out.println("join:" + completableFuture.join());
    }


    /**
     * 测试内容: 测试 thenApply 完成后执行，可以对参数进行传递
     * 异常：因存在依赖当某一步出错会停止执行
     * 测试结果: 执行完成：买鱼、买调料、做饭= 酸菜鱼
     */
    @Test
    public void test02() throws Exception {
        CompletableFuture.supplyAsync(() -> "买鱼")
                .thenApply(s -> s + "、买调料")
                .thenApply(s -> s + "、做饭")
                .thenApply(s -> s + "= 酸菜鱼")
                .whenComplete((s, e) -> {
                    if (e == null) {
                        System.out.println("执行完成：" + s);
                    }
                }).exceptionally(throwable -> {
            throwable.printStackTrace();
            return null;
        });
    }

    /**
     * 测试内容: 测试 handle 完成后执行，可以对参数进行传递
     * 异常：出现异常不会导致停止，但是会导致前面结果为 null(手动处理异常可以保留)
     * 测试结果: 执行完成：买鱼、买调料、做饭= 酸菜鱼
     */
    @Test
    public void test04() throws Exception {
        CompletableFuture.supplyAsync(() -> "买鱼")
                .handle((s, e) -> {
                    try {
                        int i = 1 / 0;
                        return s + "、买调料";
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    } finally {
                        return s + "、买调料";
                    }
                })
                .handle((s, e) -> s + "、做饭")
                .handle((s, e) -> s + "= 酸菜鱼")
                .whenComplete((s, e) -> {
                    if (e == null) {
                        System.out.println("执行完成：" + s);
                    }
                }).exceptionally(throwable -> {
            throwable.printStackTrace();
            return null;
        });
    }


    /**
     * 测试内容: 测试 thenAccept 完成后执行，对返回结果进行修改 无法传递参数
     * 测试结果: 酸菜鱼
     */
    @Test
    public void test03() throws Exception {
        CompletableFuture.supplyAsync(() -> "买鱼")
                .thenApply(s -> "酸菜鱼")
                .thenAccept(System.out::println);
    }

    /**
     * 测试内容: 测试 thenRun thenAccept thenApply
     * 测试结果:
     */
    @Test
    public void test() throws Exception {
        // thenApply 链式调用有返回值 Function 接口
        String str1 = CompletableFuture.supplyAsync(() -> "买鱼").thenApply(s -> "酸菜鱼").join();
        System.out.println(str1);

        // thenAccept 链式调用无返回值 Consumer 接口
        CompletableFuture.supplyAsync(() -> "买鱼").thenAccept(System.out::println);

        // thenRun 链式调用无返回值 Runnable 接口
        CompletableFuture.supplyAsync(() -> "买鱼").thenRun(() -> {
            System.out.println("买完鱼去买菜");
        });
    }

    /**
     * 测试内容: thenRun 线程池使用情况
     * 测试结果: Thread[ForkJoinPool.commonPool-worker-1,5,main]
     * Thread[main,5,main]
     */
    @Test
    public void test05() throws Exception {
        CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread());
            return Thread.currentThread();
        }).thenRun(() -> {
            System.out.println(Thread.currentThread());
        });
    }

    /**
     * 测试内容: thenRunAsync 使用自定义线程池
     * 测试结果: Thread[pool-1-thread-1,5,main]
     * Thread[pool-1-thread-2,5,main]
     */
    @Test
    public void test06() throws Exception {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 4,
                30L, TimeUnit.SECONDS, new ArrayBlockingQueue(10));
        CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread());
            return Thread.currentThread();
        }, executor).thenRunAsync(() -> {
            System.out.println(Thread.currentThread());
        });
    }


    /**
     * 测试内容: applyToEither 选择优先完成的任务
     * 测试结果: 任务 B 完成
     */
    @Test
    public void test07() throws Exception {
        CompletableFuture<String> completableFutureA = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务 A 完成";
        });

        CompletableFuture<String> completableFutureB = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务 B 完成";
        });

        System.out.println(completableFutureA
                .applyToEither(completableFutureB, f -> f)
                .join());
    }

    /**
     * 测试内容: CompletionStage 对计算结果进行合并
     * 测试结果: hello-world
     */
    @Test
    public void test08() throws Exception {
        CompletableFuture<String> completableFutureA = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello";
        });

        CompletableFuture<String> completableFutureB = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "world";
        });

        String join = completableFutureA
                .thenCombine(completableFutureB, (a, b) -> a.concat("-").concat(b))
                .join();
        System.out.println(join);
    }
}
